package elements;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.swing.JOptionPane;

import level.Level;
import level.LevelManager;

public class Stage implements Drawable{
	TileMap tileMap;
	String [][] map ;
	Element [][] elements ;    // this array is used for quick look up by indeies
	ArrayList<Element> laserList = new ArrayList<Element>();
	ArrayList<Element> wallList = new ArrayList<Element>();
	ArrayList<Element> normaList =  new ArrayList<Element>();
	ArrayList<Coin> coinList =  new ArrayList<Coin>();
	Map<String ,Light> lightsMap = new HashMap<String, Light>();
	Element currentSelected ;
	int currentLevelIndex =1;
	Level currentLevel ;
	public Stage(){
		tileMap = new TileMap();
		intiStage();
	}
	
	public void intiStage(){
		laserList.clear();
		normaList.clear();
		wallList.clear();
		coinList.clear();
		currentSelected=null;
		elements =new Element[TileMap.ROWS][TileMap.COLS];
		loadMap(currentLevelIndex);
		addElement();
		generateLight();
	}
	
	public void createElement(int i,int j,String str){
		String[] arr= str.split("-");
		int type = Integer.valueOf(arr[0]);
		int color = Integer.parseInt(arr[1]);
		int direction = Integer.valueOf(arr[2]);
		int actType = Integer.valueOf(arr[3]);
		Element element = null;
		switch (type) {
			case Element.LASER:element= new Laser(i, j, color, direction,actType);laserList.add(element);break;
			case Element.WALL1:element= new Wall1(i, j, color, direction,actType);wallList.add(element);break;
			case Element.WALL2:element= new Wall2(i, j, color, direction,actType);wallList.add(element);break;
			case Element.WALL3:element= new Wall3(i, j, color, direction,actType);wallList.add(element);break;
			case Element.COIN:element= new Coin(i, j, color, direction,actType);coinList.add((Coin)element);break;
			case Element.BLOCK:element= new Block(i, j, color, direction,actType);normaList.add(element);break;
			case Element.MIRROR:element= new Mirror(i, j, color, direction,actType);normaList.add(element);break;
			case Element.DMIRROR:element= new DMirror(i, j, color, direction,actType);normaList.add(element);break;
			case Element.TWOWAY1:element= new Twoway1(i, j, color, direction,actType);normaList.add(element);break;
			case Element.TWOWAY2:element= new Twoway2(i, j, color, direction,actType);normaList.add(element);break;
			case Element.THREEWAY:element= new Threeway(i, j, color, direction,actType);normaList.add(element);break;
			case Element.EIGHTWAY:element= new Eightway(i, j, color, direction,actType);normaList.add(element);break;
			case Element.STRAIGHT:element= new Straight(i, j, color, direction,actType);normaList.add(element);break;
			case Element.CROSS:element= new Cross(i, j, color, direction,actType);normaList.add(element);break;
			case Element.REFRACTION:element= new Refraction(i, j, color, direction,actType);normaList.add(element);break;
		}
		elements[i][j]=element;
		map[i][j]=""+type+"-"+color+"-"+direction+"-"+actType;
	}
	public void addElement(){
		for(int i =0;i<map.length;i++){
			for(int j = 0;j<map[i].length;j++){
				String str = map[i][j];
				if(!"".equals(str)){
					createElement(i,j,str);
					
				}
			}
		}
		
	}
	
	public ArrayList<Coin> getCoinList() {
		return coinList;
	}

	public String[][] getMap() {
		return map;
	}
	
	public void generateLight(){
		lightsMap.clear();
		for(int i =0; i<coinList.size();i++){
			coinList.get(i).acceptColor.clear();
			coinList.get(i).acceptDirection.clear();
		}
		for(int i =0;i<laserList.size();i++){
			Element element = laserList.get(i);
			if(((Laser)element).show)addLight(element.rindex,element.cindex,element.direction,element.color);
		}
	}
//recursion
	public void addLight(int i,int j, int direction,int color){
		int[] step = getStep(direction);
		int starti = i;
		int startj = j;
		i+=step[0];
		j+=step[1];
		while(i>=0&&i<TileMap.ROWS&&j>=0&&j<TileMap.COLS){
			if(elements[i][j]!=null){
				if(elements[i][j].type==Element.COIN){
					((Coin)elements[i][j]).accept(color,direction);
				}
				if(!elements[i][j].canGo(direction)){
					int[][] output = elements[i][j].output(direction, color);
					if(output!=null){
						for(int k=0;k<output.length;k++){
							addLight(i,j, output[k][0],output[k][1]);
						}
					}
					break;
				}
			}
			i+=step[0];
			j+=step[1];
		}
		//if light exist, mix the color of it.
		if(lightsMap.containsKey(""+starti+startj+i+j)||lightsMap.containsKey(""+i+j+starti+startj)){
			Light light = lightsMap.get(""+starti+startj+i+j)!=null?lightsMap.get(""+starti+startj+i+j):lightsMap.get(""+i+j+starti+startj);
			light.color = light.color|color;
		}else{
			lightsMap.put(""+starti+startj+i+j,new Light(starti,startj,i,j,color));
		}
	}
	
	public int[] getStep(int direction){
		int[] step =null;
		switch (direction) {
		  case 1:	step = new int[]{-1,0};	break;
		  case 2:	step = new int[]{-1,1};	break;
		  case 3:	step = new int[]{0,1};	break;
		  case 4:	step = new int[]{1,1};	break;
		  case 5:	step = new int[]{1,0};	break;
		  case 6:	step = new int[]{1,-1};	break;
		  case 7:	step = new int[]{0,-1};	break;
		  case 8:	step = new int[]{-1,-1};break;
		}
		return step;
	}
	public boolean checkWin(){
		
		for(int i=0;i<coinList.size();i++){
			if(!coinList.get(i).isAccept())return false;
		}
		return true;
	}
	@Override
	public void draw(Graphics2D g) {
		// TODO Auto-generated method stub
		tileMap.draw(g);
		drawLight(g);
		drawWall(g);
		drawCoin(g);
		drawLaser(g);
		drawItem(g);
		g.setColor(Color.white);
		g.drawString("Current Level: "+ this.getCurrentLevelIndex()+"/"+LevelManager.getLevelCount(),900, TileMap.ROWS*TileMap.CELLWIDTH+TileMap.offsetY );
		if(checkWin()){
			g.drawString("Level passed ",600,400);
		}
	}
	public void drawCoin(Graphics2D g){
		for(int i =0; i<coinList.size();i++){
			coinList.get(i).draw(g);
		}
	}
	public void drawLaser(Graphics2D g){
		for(int i =0; i<laserList.size();i++){
			laserList.get(i).draw(g);
		}
	}
	public void drawWall(Graphics2D g){
		for(int i =0; i<wallList.size();i++){
			wallList.get(i).draw(g);
		}
	}
	public void drawItem(Graphics2D g){
		for(int i =0; i<normaList.size();i++){
			normaList.get(i).draw(g);
		}
	}
	public void drawLight(Graphics2D g){
	    Set<String> set = lightsMap.keySet();
	    Iterator<String> iterator = set.iterator();
	    while (iterator.hasNext()) {
			String id = (String) iterator.next();
			Light light = lightsMap.get(id);
			light.draw(g);
		}
	}
	public void loadMap(int i ){
		if(i==0){
			templateLevel();
		}else{
		currentLevel =  LevelManager.getLevel(currentLevelIndex);
		map =currentLevel.getMap();
		}
	}
	public int getCurrentLevelIndex(){
		return currentLevelIndex;
	}
	public Level getCurrentLevel(){
		return currentLevel;
	}
	public void newLevel(){
		currentLevelIndex=0;
		this.intiStage();
	}
	public void nextLevel(){
		currentLevelIndex =(currentLevelIndex==LevelManager.getLevelCount())?1:currentLevelIndex+1;
		this.intiStage();
	}
	public void prevLevel(){
		currentLevelIndex = (currentLevelIndex==1)?LevelManager.getLevelCount():currentLevelIndex-1;
		this.intiStage();
	}
	public void setCurrentLevelIndex(int i){
		currentLevelIndex=i;
	}
	public void templateLevel(){
		map =new String[][]{
				{"2-0-3-4","1-0-1-4","1-0-1-4","3-0-5-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","2-0-5-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"1-0-3-4","","","1-0-3-4","","","","","","","","","","","","","","","","","","","1-0-3-4"},
				{"2-0-1-4","1-0-1-4","1-0-1-4","3-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","1-0-1-4","2-0-7-4"},
		};
	}
	public ArrayList<Element> getLaserList() {
		return laserList;
	}

	public ArrayList<Element> getNormaList() {
		return normaList;
	}

	public Element getCurrentSelected() {
		return currentSelected;
	}

	public void setCurrentSelected(Element currentSelected) {
		this.currentSelected = currentSelected;
	}

	public Element[][] getElements() {
		return elements;
	}
}
